home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / util / DialRules.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  10KB  |  397 lines

  1. /*    $Header: /usr/people/sam/fax/util/RCS/DialRules.c++,v 1.12 1994/02/28 14:24:08 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25.  
  26. /*
  27.  * FlexFAX Dialing String Rule Support.
  28.  */
  29. #include "DialRules.h"
  30. #include "RegExArray.h"
  31. #include "Dictionary.h"
  32.  
  33. #include <InterViews/regexp.h>
  34. #include <ctype.h>
  35.  
  36. /*
  37.  * Interpolation points in replacement strings have
  38.  * the META bit or'd together with their match identifier.
  39.  * That is ``\1'' is META|1, ``\2'' is META|2, etc.
  40.  * The ``&'' interpolation is treated as ``\0''.
  41.  */
  42. const u_int META = 0200;        // interpolation marker
  43.  
  44. struct DialRule {
  45.     RegExPtr    pat;            // pattern to match
  46.     fxStr    replace;        // replacement string
  47.  
  48.     DialRule();
  49.     ~DialRule();
  50.  
  51.     int compare(const DialRule *) const;
  52. };
  53. DialRule::DialRule(){}
  54. DialRule::~DialRule(){}
  55. int DialRule::compare(const DialRule*) const { return 0; }
  56.  
  57. fxDECLARE_ObjArray(RuleArray, DialRule);
  58. fxIMPLEMENT_ObjArray(RuleArray, DialRule);
  59. fxDECLARE_StrKeyDictionary(VarDict, fxStr);
  60. fxIMPLEMENT_StrKeyObjValueDictionary(VarDict, fxStr);
  61. fxDECLARE_StrKeyDictionary(RulesDict, RuleArrayPtr);
  62. fxIMPLEMENT_StrKeyObjValueDictionary(RulesDict, RuleArrayPtr);
  63.  
  64. DialStringRules::DialStringRules(const char* file) : filename(file)
  65. {
  66.     verbose = FALSE;
  67.     fp = NULL;
  68.     vars = new VarDict;
  69.     regex = new RegExArray;
  70.     rules = new RulesDict;
  71. }
  72.  
  73. DialStringRules::~DialStringRules()
  74. {
  75.     delete rules;
  76.     delete regex;
  77.     delete vars;
  78. }
  79.  
  80. void DialStringRules::setVerbose(fxBool b) { verbose = b; }
  81.  
  82. /*
  83.  * Parse the dialing string rules file
  84.  * to create the in-memory rules.
  85.  */
  86. fxBool
  87. DialStringRules::parse()
  88. {
  89.     fxBool ok = FALSE;
  90.     fp = fopen(filename, "r");
  91.     if (fp) {
  92.     ok = parseRules();
  93.     fclose(fp);
  94.     }
  95.     return (ok);
  96. }
  97.  
  98. void
  99. DialStringRules::def(const fxStr& var, const fxStr& value)
  100. {
  101.     if (verbose)
  102.     printf("Define %s = \"%s\"\n", (char*) var, (char*) value);
  103.     (*vars)[var] = value;
  104. }
  105.  
  106. void
  107. DialStringRules::undef(const fxStr& var)
  108. {
  109.     if (verbose)
  110.     printf("Undefine %s\n", (char*) var);
  111.     vars->remove(var);
  112. }
  113.  
  114. fxBool
  115. DialStringRules::parseRules()
  116. {
  117.     lineno = 0;
  118.     char line[1024];
  119.     char* cp;
  120.     while (cp = nextLine(line, sizeof (line))) {
  121.     // collect token
  122.     if (!isalpha(*cp)) {
  123.         parseError("Syntax error, expecting identifier");
  124.         return (FALSE);
  125.     }
  126.     const char* tp = cp;
  127.     for (cp++; isalnum(*cp); cp++)
  128.         ;
  129.     fxStr var(tp, cp-tp);
  130.     while (isspace(*cp))
  131.         cp++;
  132.     if (*cp == ':' && cp[1] == '=') {    // rule set definition
  133.         for (cp += 2; *cp != '['; cp++)
  134.         if (*cp == '\0') {
  135.             parseError("Missing '[' while parsing rule set");
  136.             return (FALSE);
  137.         }
  138.         if (verbose)
  139.         printf("%s := [\n", (char*) var);
  140.         RuleArray* ra = new RuleArray;
  141.         if (!parseRuleSet(*ra)) {
  142.         delete ra;
  143.         return (FALSE);
  144.         }
  145.         (*rules)[var] = ra;
  146.         if (verbose)
  147.         printf("]\n");
  148.     } else if (*cp == '=') {        // variable definition
  149.         fxStr value;
  150.         if (parseToken(cp+1, value) == NULL)
  151.         return (FALSE);
  152.         def(var, value);
  153.     } else {                // an error
  154.         parseError("Missing '=' or ':=' after \"%s\"", (char*) var);
  155.         return (FALSE);
  156.     }
  157.     }
  158.     if (verbose) {
  159.     RuleArray* ra = (*rules)["CanonicalNumber"];
  160.     if (ra == 0)
  161.         fprintf(stderr, "Warning, no \"CanonicalNumber\" rules.\n");
  162.     ra = (*rules)["DialString"];
  163.     if (ra == 0)
  164.         fprintf(stderr, "Warning, no \"DialString\" rules.\n");
  165.     }
  166.     return (TRUE);
  167. }
  168.  
  169. /*
  170.  * Locate the next non-blank line in the file
  171.  * and return a pointer to the first non-whitespace
  172.  * character on the line.  Leading white space and
  173.  * comments are stripped.  NULL is returned on EOF.
  174.  */
  175. char*
  176. DialStringRules::nextLine(char* line, int lineSize)
  177. {
  178.     char* cp;
  179.     do {
  180.     if (!fgets(line, lineSize, fp))
  181.         return (NULL);
  182.     lineno++;
  183.     cp = strchr(line, '!');
  184.     if (cp)
  185.         *cp = '\0';
  186.     else if (cp = strchr(line, '\n'))
  187.         *cp = '\0';
  188.     for (cp = line; isspace(*cp); cp++)
  189.         ;
  190.     } while (*cp == '\0');
  191.     return (cp);
  192. }
  193.  
  194. /*
  195.  * Parse the next token on the input line and return
  196.  * the result in the string v.  Variable references
  197.  * are interpolated here, though they might be better
  198.  * done at a later time.
  199.  */
  200. const char*
  201. DialStringRules::parseToken(const char* cp, fxStr& v)
  202. {
  203.     while (isspace(*cp))
  204.     cp++;
  205.     const char* tp;
  206.     if (*cp == '"') {            // "..."
  207.     tp = ++cp;
  208.     for (;;) {
  209.         if (*cp == '\0') {
  210.         parseError("String with unmatched '\"'");
  211.         return (NULL);
  212.         }
  213.         if (*cp == '\\' && cp[1] == '\0') {
  214.         parseError("Bad '\\' escape sequence");
  215.         return (NULL);
  216.         }
  217.         if (*cp == '"' && (cp == tp || cp[-1] != '\\'))
  218.         break;
  219.         cp++;
  220.     }
  221.     v = fxStr(tp, cp-tp);
  222.     cp++;                // skip trailing ``"''
  223.     } else {                // token terminated by white space
  224.     for (tp = cp; *cp != '\0'; cp++) {
  225.         if (*cp == '\\' && cp[1] == '\0') {
  226.         parseError("Bad '\\' escape sequence");
  227.         return (NULL);
  228.         }
  229.         if (isspace(*cp) && (cp == tp || cp[-1] != '\\'))
  230.         break;
  231.     }
  232.     v = fxStr(tp, cp-tp);
  233.     }
  234.     for (u_int i = 0, n = v.length(); i < n; i++) {
  235.     if (v[i] == '$' && (i+1<n && v[i+1] == '{')) {
  236.         /*
  237.          * Handle variable reference.
  238.          */
  239.         u_int l = v.next(i, '}');
  240.         if (l >= v.length()) {
  241.         parseError("Missing '}' for variable reference");
  242.         return (FALSE);
  243.         }
  244.         fxStr var = v.cut(i+2,l-(i+2));// variable name
  245.         v.remove(i, 3);        // remove ${}
  246.         const fxStr& value = (*vars)[var];
  247.         v.insert(value, i);        // insert variable's value
  248.         n = v.length();        // adjust string length
  249.         i += value.length()-1;    // don't scan substituted string
  250.     } else if (v[i] == '\\')    // pass \<char> escapes unchanged
  251.         i++;
  252.     }
  253.     return cp;
  254. }
  255.  
  256. /*
  257.  * Handle \escapes for a token
  258.  * that appears on the RHS of a rewriting rule.
  259.  */
  260. void
  261. DialStringRules::subRHS(fxStr& v)
  262. {
  263.     /*
  264.      * Replace `&' and \n items with (META|<n>) to mark spots
  265.      * in the token where matches should be interpolated.
  266.      */
  267.     for (u_int i = 0, n = v.length(); i < n; i++) {
  268.     if (v[i] == '\\') {        // process \<char> escapes
  269.         v.remove(i), n--;
  270.         if (isdigit(v[i]))
  271.         v[i] = META | (v[i] - '0');
  272.     } else if (v[i] == '&')
  273.         v[i] = META | 0;
  274.     }
  275. }
  276.  
  277. /*
  278.  * Parse a set of rules and construct the array of
  279.  * rules (regular expressions + replacement strings).
  280.  */
  281. fxBool
  282. DialStringRules::parseRuleSet(RuleArray& rules)
  283. {
  284.     for (;;) {
  285.     char line[1024];
  286.     const char* cp = nextLine(line, sizeof (line));
  287.     if (!cp) {
  288.         parseError("Missing ']' while parsing rule set");
  289.         return (FALSE);
  290.     }
  291.     if (*cp == ']')
  292.         return (TRUE);
  293.     // new rule
  294.     fxStr pat;
  295.     if ((cp = parseToken(cp, pat)) == NULL)
  296.         return (FALSE);
  297.     while (isspace(*cp))
  298.         cp++;
  299.     if (*cp != '=') {
  300.         parseError("Rule pattern without '='");
  301.         return (FALSE);
  302.     }
  303.     DialRule r;
  304.     if (parseToken(cp+1, r.replace) == NULL)
  305.         return (FALSE);
  306.     if (verbose)
  307.         printf("  \"%s\" = \"%s\"\n",
  308.         (char*) pat, (char*) r.replace);
  309.     subRHS(r.replace);
  310.     for (u_int i = 0, n = regex->length(); i < n; i++) {
  311.         RegEx* re = (*regex)[i];
  312.         if (strcmp(re->pattern(), pat) == 0)
  313.         break;
  314.     }
  315.     if (i >= n) {
  316.         r.pat = new RegEx(pat, pat.length());
  317.         regex->append(r.pat);
  318.     } else
  319.         r.pat = (*regex)[i];
  320.     rules.append(r);
  321.     }
  322. }
  323.  
  324. #include <stdarg.h>
  325.  
  326. void
  327. DialStringRules::parseError(const char* fmt ...)
  328. {
  329.     va_list ap;
  330.     va_start(ap, fmt);
  331.     fprintf(stderr, "%s: line %u: ", (char*) filename, lineno); 
  332.     vfprintf(stderr, fmt, ap);
  333.     va_end(ap);
  334.     putc('\n', stderr);
  335. }
  336.  
  337. fxStr
  338. DialStringRules::applyRules(const fxStr& name, const fxStr& s)
  339. {
  340.     fxStr result(s);
  341.     RuleArray* ra = (*rules)[name];
  342.     if (ra) {
  343.     for (u_int i = 0, n = (*ra).length(); i < n; i++) {
  344.         DialRule& rule = (*ra)[i];
  345.         u_int len = result.length();
  346.         u_int start = 0;
  347.         while (rule.pat->Search(result, len, start, len) >= 0) {
  348.         /*
  349.          * Regular expression match.
  350.          */
  351.         int ix = rule.pat->BeginningOfMatch();
  352.         int len = rule.pat->EndOfMatch() - ix;
  353.         if (len == 0)        // avoid looping on zero-length matches
  354.             break;
  355.         /*
  356.          * Do ``&'' and ``\n'' interpolations in the
  357.          * replacement.  NB: & is treated as \0.
  358.          */
  359.         fxStr replace(rule.replace);
  360.         for (u_int ri = 0, rlen = replace.length(); ri < rlen; ri++)
  361.             if (replace[ri] & META) {
  362.             u_char mn = replace[ri] &~ META;
  363.             int ms = rule.pat->BeginningOfMatch(mn);
  364.             int mlen = rule.pat->EndOfMatch(mn) - ms;
  365.             replace.remove(ri);    // delete & or \n
  366.             replace.insert(result.extract(ms, mlen), ri);
  367.             rlen = replace.length();// adjust string length ...
  368.             ri += mlen - 1;        // ... and scan index
  369.             }
  370.         /*
  371.          * Paste interpolated replacement string over match.
  372.          */
  373.         result.remove(ix, len);
  374.         result.insert(replace, ix);
  375.         start = ix + replace.length();    // skip replace when searching
  376.         }
  377.     }
  378.     }
  379.     return result;
  380. }
  381.  
  382. /*
  383.  * Apply the canonical number rule set to a string.
  384.  */
  385. fxStr DialStringRules::canonicalNumber(const fxStr& s)
  386.     { return applyRules("CanonicalNumber", s); }
  387. /*
  388.  * Apply the dial string rule set to a string.
  389.  */
  390. fxStr DialStringRules::dialString(const fxStr& s)
  391.     { return applyRules("DialString", s); }
  392. /*
  393.  * Apply the display number rule set to a string.
  394.  */
  395. fxStr DialStringRules::displayNumber(const fxStr& s)
  396.     { return applyRules("DisplayNumber", s); }
  397.